package org.mm.proxycache.http;

import static com.google.common.base.Objects.equal;

import java.util.concurrent.atomic.AtomicReference;

import com.google.common.base.Objects;

public class HttpUrl
{
    private String schema;
    private String host;
    private int port = -1;
    private String user;
    private String password;
    
    private String path;
    private String query;
    private String anchor;
    
    private AtomicReference<String> composedUrl = new AtomicReference<String>();
    
    public HttpUrl()
    {
    }
    
    public static HttpUrl parse( String s ) throws IllegalArgumentException
    {
        HttpUrl url = new HttpUrl();
        url.composedUrl.set( s );
        boolean dontHavePath = false;
        
        int n = s.indexOf( '#' );
        if ( n > 0 )
        {
            url.anchor = s.substring( n + 1 );
            s = s.substring( 0, n );
        }
        
        n = s.indexOf( "://" );
        if ( n > 0 )
        {
            url.schema = s.substring( 0, n );
            s = s.substring( n + 3 );
        }
        
        n = s.lastIndexOf( '?' );
        if ( n > 0 )
        {
            url.query = s.substring( n + 1 );
            s = s.substring( 0, n );
        }
        
        n = s.indexOf( '/' );
        if ( n > 0 )
        {
            url.host = s.substring( 0, n );
            s = s.substring( n + 1 );
        }
        else if ( url.schema != null && n != 0 )
        {
            url.host = s;
            s = "";
        }
        else if ( n < 0 )
        {
            url.host = s;
            s = "";
            dontHavePath = true;
        }
        
        if ( url.host == null )
        {
            url.path = s;
            return url;
        }
        
        n = url.host.indexOf( '@' );
        if ( n > 0 )
        {
            url.user = url.host.substring( 0, n );
            url.host = url.host.substring( n + 1 );
            
            n = url.user.indexOf( ':' );
            if ( n > 0 )
            {
                url.password = url.user.substring( n + 1 );
                url.user = url.user.substring( 0, n );
            }
        }
        
        n = url.host.lastIndexOf( ':' );
        if ( n > 0 )
        {
            url.port = Integer.parseInt( url.host.substring( n + 1 ) );
            url.host = url.host.substring( 0, n );
        }
        
        if ( !dontHavePath )
            url.path = "/" + s;
        
        return url;
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString()
    {
        String s = composedUrl.get();
        if ( s != null )
            return s;
        
        StringBuilder sb = new StringBuilder( 40 );
        if ( schema != null )
            sb.append( schema ).append( "://" );
        
        if ( user != null )
        {
            sb.append( user );
            if ( password != null )
                sb.append( ':' ).append( password );
            sb.append( '@' );
        }
        
        if ( host != null )
        {
            sb.append( host );
            if ( port >= 0 )
                sb.append( ':' ).append( port );
        }
        
        if ( path != null )
        {
            sb.append( path );
            if ( query != null )
                sb.append( '?' ).append( query );
            if ( anchor != null )
                sb.append( '#' ).append( anchor );
        }
        
        s = sb.toString();
        composedUrl.set( s );
        return s;
    }
    
    /**
     * @return the schema
     */
    public String schema()
    {
        return schema;
    }
    
    /**
     * @param schema
     *            the schema to set
     */
    public void setSchema( String schema )
    {
        this.schema = schema;
        this.composedUrl.set( null );
    }
    
    /**
     * @return the host
     */
    public String host()
    {
        return host;
    }
    
    /**
     * @param host
     *            the host to set
     */
    public void setHost( String host )
    {
        if ( host != null )
        {
            String parts[] = host.split( ":" );
            this.host = parts[0];
            if ( parts.length > 1 )
                this.port = Integer.parseInt( parts[1] );
        }
        else
            this.host = host;
        
        this.composedUrl.set( null );
    }
    
    /**
     * @return the port
     */
    public int port()
    {
        return port;
    }
    
    /**
     * @param port
     *            the port to set
     */
    public void setPort( int port )
    {
        this.port = port;
        this.composedUrl.set( null );
    }
    
    /**
     * @return the user
     */
    public String user()
    {
        return user;
    }
    
    /**
     * @param user
     *            the user to set
     */
    public void setUser( String user )
    {
        this.user = user;
        this.composedUrl.set( null );
    }
    
    /**
     * @return the password
     */
    public String password()
    {
        return password;
    }
    
    /**
     * @param password
     *            the password to set
     */
    public void setPassword( String password )
    {
        this.password = password;
        this.composedUrl.set( null );
    }
    
    /**
     * @return the path
     */
    public String path()
    {
        return path;
    }
    
    /**
     * @param path
     *            the path to set
     */
    public void setPath( String path )
    {
        this.path = path;
        this.composedUrl.set( null );
    }
    
    /**
     * @return the query
     */
    public String query()
    {
        return query;
    }
    
    /**
     * @param query
     *            the query to set
     */
    public void setQuery( String query )
    {
        this.query = query;
        this.composedUrl.set( null );
    }
    
    /**
     * @return the anchor
     */
    public String anchor()
    {
        return anchor;
    }
    
    /**
     * @param anchor
     *            the anchor to set
     */
    public void setAnchor( String anchor )
    {
        this.anchor = anchor;
        this.composedUrl.set( null );
    }
    
    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#clone()
     */
    @Override
    public HttpUrl clone()
    {
        HttpUrl url = new HttpUrl();
        url.schema = schema;
        url.host = host;
        url.port = port;
        url.user = user;
        url.password = password;
        url.path = path;
        url.query = query;
        url.anchor = anchor;
        url.composedUrl.set( composedUrl.get() );
        return url;
    }
    
    @Override
    public boolean equals( Object obj )
    {
        if ( obj instanceof HttpUrl )
        {
            HttpUrl o = (HttpUrl) obj;
            return equal( schema, o.schema ) && equal( host, o.host ) && equal( port, o.port )
                    && equal( user, o.user ) && equal( password, o.password )
                    && equal( path, o.path ) && equal( query, o.query ) && equal( anchor, o.anchor );
        }
        
        return false;
    }
    
    @Override
    public int hashCode()
    {
        return Objects.hashCode( schema, host, port, user, password, path, query, anchor );
    }
    
}
